<script setup lang="ts">
import type { Component, VNode } from 'vue';

import { computed } from 'vue';

import {
  ElCard,
  ElDescriptions,
  ElDescriptionsItem,
  ElSkeleton,
} from 'element-plus';

// 直接在组件中定义类型
interface DescriptionItemSchema {
  field?: string;
  label: string;
  hidden?: ((data: any) => boolean) | boolean;
  content?: (data: any) => any | Component | VNode;
  span?: number;
  labelClass?: string;
  contentClass?: string;
  formatter?: (value: any, data: any) => string;
  labelWidth?: number | string;
  minWidth?: number | string;
}
interface EnhancedDescriptionProps {
  schema: DescriptionItemSchema[];
  data: Record<string, any>;
  column?: number;
  border?: boolean;
  direction?: 'horizontal' | 'vertical';
  size?: 'default' | 'large' | 'small';
  title?: string;
  extra?: string;
  card?: boolean;
  cardTitle?: string;
  cardClass?: string;
  emptyText?: string;
  loading?: boolean;
  labelWidth?: number | string;
}

// 定义 props 的默认值
const props = withDefaults(defineProps<EnhancedDescriptionProps>(), {
  column: () => 3,
  border: () => true,
  direction: () => 'horizontal' as const,
  size: () => 'default' as const,
  card: () => false,
  loading: () => false,
  emptyText: () => '-',
  labelWidth: () => 'auto',
});
// 定义 emits
const emit = defineEmits<{
  itemClick: [item: DescriptionItemSchema, data: any];
}>();

// 计算可见的 schema 项
const visibleSchema = computed(() => {
  const data = props.data || {};
  return props.schema.filter((item) => {
    if (typeof item.hidden === 'function') {
      return !item.hidden(data);
    }
    return !item.hidden;
  });
});

// 获取项的唯一 key
const getItemKey = (item: DescriptionItemSchema): string => {
  return item.field || item.label;
};

// 获取插槽名称
const getSlotName = (item: DescriptionItemSchema): string => {
  return item.field || item.label;
};

// 获取渲染的组件
const getRenderedComponent = (
  item: DescriptionItemSchema,
): (() => VNode) | Component | null => {
  const data = props.data || {};

  try {
    if (item.content) {
      const contentResult = item.content(data);

      // 检查是否是 VNode 或组件
      if (
        contentResult &&
        ((typeof contentResult === 'object' && 'type' in contentResult) ||
          typeof contentResult === 'function')
      ) {
        return contentResult as (() => VNode) | Component;
      }
    }
  } catch (error) {
    console.error(`Error rendering component for ${item.label}:`, error);
  }

  return null;
};

// 获取渲染的文本
const getRenderedText = (item: DescriptionItemSchema): string => {
  const data = props.data || {};

  try {
    if (item.content) {
      const contentResult = item.content(data);

      // 如果不是组件，返回文本
      if (
        !(
          contentResult &&
          ((typeof contentResult === 'object' && 'type' in contentResult) ||
            typeof contentResult === 'function')
        )
      ) {
        return contentResult ?? props.emptyText;
      }
    }

    if (item.formatter && item.field) {
      const value = data[item.field];
      return item.formatter(value, data) ?? props.emptyText;
    }

    if (item.field) {
      return data[item.field] ?? props.emptyText;
    }

    return props.emptyText;
  } catch (error) {
    console.error(`Error rendering text for ${item.label}:`, error);
    return props.emptyText;
  }
};
// 获取项的宽度
const getItemlabelWidth = (item: DescriptionItemSchema) => {
  return item.labelWidth || '100';
};
const getItemlabeMinWidth = (item: DescriptionItemSchema) => {
  return item.minWidth || '100';
};
// 项点击事件
const handleItemClick = (item: DescriptionItemSchema) => {
  emit('itemClick', item, props.data);
};
</script>

<template>
  <!-- 卡片模式 -->
  <ElCard
    v-if="props.card"
    :header="props.cardTitle"
    shadow="never"
    :class="props.cardClass"
  >
    <template #header v-if="$slots.header">
      <slot name="header" :data="props.data" :schema="props.schema"></slot>
    </template>

    <ElSkeleton :loading="props.loading" animated :rows="10">
      <template #default>
        <ElDescriptions
          :column="props.column"
          :border="props.border"
          :direction="props.direction"
          :size="props.size"
        >
          <template v-for="item in visibleSchema" :key="getItemKey(item)">
            <ElDescriptionsItem
              :label="item.label"
              :span="props.column === 1 ? 24 : item.span"
              :label-class="item.labelClass"
              :class="item.contentClass"
              :label-width="getItemlabelWidth(item)"
              :min-width="getItemlabeMinWidth(item)"
            >
              <slot
                v-if="$slots[getSlotName(item)]"
                :name="getSlotName(item)"
                :data="props.data"
                :item="item"
              ></slot>
              <template v-else>
                <component
                  :is="getRenderedComponent(item)"
                  v-if="getRenderedComponent(item)"
                />
                <span v-else>
                  {{ getRenderedText(item) }}
                </span>
              </template>
            </ElDescriptionsItem>
          </template>
        </ElDescriptions>
      </template>
    </ElSkeleton>
  </ElCard>

  <!-- 非卡片模式 -->
  <ElSkeleton v-else :loading="props.loading" animated :rows="10">
    <template #default>
      <ElDescriptions
        :title="props.title"
        :column="props.column"
        :border="props.border"
        :direction="props.direction"
        :size="props.size"
        :extra="props.extra"
      >
        <template #title v-if="$slots.title">
          <slot name="title" :data="props.data" :schema="props.schema"></slot>
        </template>

        <template #extra v-if="$slots.extra">
          <slot name="extra" :data="props.data" :schema="props.schema"></slot>
        </template>

        <template v-for="item in visibleSchema" :key="getItemKey(item)">
          <ElDescriptionsItem
            :label="item.label"
            :span="props.column === 1 ? 24 : item.span"
            :label-class="item.labelClass"
            :class="item.contentClass"
            :label-width="getItemlabelWidth(item)"
            :min-width="getItemlabeMinWidth(item)"
          >
            <slot
              v-if="$slots[getSlotName(item)]"
              :name="getSlotName(item)"
              :data="props.data"
              :item="item"
            ></slot>
            <template v-else>
              <component
                :is="getRenderedComponent(item)"
                v-if="getRenderedComponent(item)"
              />
              <span v-else>
                {{ getRenderedText(item) }}
              </span>
            </template>
          </ElDescriptionsItem>
        </template>
      </ElDescriptions>
    </template>
  </ElSkeleton>
</template>

<style scoped>
:deep(.ElDescriptions__body) {
  background-color: transparent;
}

:deep(.ElDescriptions__label) {
  font-weight: 500;
}
</style>
